﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Bamboo.Utils;

namespace Bamboo.ExeShell
{
    /// <summary>
    /// 执行Oracle的impdp命令的辅助类
    /// 命令的执行示例：impdp ***@vm directory=FMDMP_FM dumpfile=FM_20200317_TEMP.dmp schemas=fm table_exists_action=replace transform=oid:n exclude=USER  job_name=tooljob_imp20200318_091154    /// </summary>
    public class OracleImpdp
    {
        /// <summary>
        /// Oracle服务器上存放dmp文件的目录
        /// </summary>
        public string DumpFileDirectory { get; set; }
        /// <summary>
        /// 备份文件名，例如xxx.dmp
        /// </summary>
        public string DumpFileName { get; set; }
        /// <summary>
        /// 备份文件的schema，例如fm
        /// </summary>
        public string DumpFileSchema { get; set; }
        /// <summary>
        /// 数据库连接服务名
        /// </summary>
        public string OracleService { get; set; }
        /// <summary>
        /// 数据库用户
        /// </summary>
        public string OracleUser { get; set; }
        /// <summary>
        /// 数据库密码
        /// </summary>
        public string OraclePassword { get; set; }
        /// <summary>
        /// Linux系统的ip地址(linux导入必填)
        /// </summary>
        public string LinuxIP { get => ExecLinux.Hostname; set => ExecLinux.Hostname = value; }
        /// <summary>
        ///  Linux系统用于执行impdp的用户名(linux导入必填)
        /// </summary>
        public string LinuxUser { get => ExecLinux.Username; set => ExecLinux.Username = value; }
        /// <summary>
        /// Linux系统的用户密码(linux导入必填)
        /// </summary>
        public string LinuxPassword { get => ExecLinux.Password; set => ExecLinux.Password = value; }

        /// <summary>
        /// 导入过程中需要排除的对象类型
        /// </summary>
        public HashSet<string> Excludes { get; } = new HashSet<string>();

        public ProcessExecute Exec { get; } = new ProcessExecute();

        public Plink ExecLinux { get; } = new Plink();

        public void ExecuteAtWindows()
        {
            Exec.FileName = "impdp.exe";
            Exec.Arguments = GetParam(false);
            Exec.IgnoreError = true;
            Exec.DebugMode = true;
            Exec.Run();
            if (Exec.ExitCode != 0)
            {
                var error = Exec.ErrorText.ToString();
                if (error.Contains("ORA-39002") && error.Contains("ORA-39168") && Excludes.Contains("USER"))
                {
                    Exec.OutputHandler?.Invoke("提示：遇到ORA-39002和ORA-39168错误，而且参数包含exclude=USER，" + 
                        "怀疑是导出dmp的用户无dba权限，下面将去掉exclude=USER重试一次。");
                    Exec.Arguments = GetParam(true);
                    Exec.Run();
                }
            }
        }

        public void ExecuteAtLinux()
        {
            string cmd = $"source .bash_profile>/dev/null; impdp " + GetParam(false);
            ExecLinux.IgnoreError = true;
            ExecLinux.ExecuteCommand(cmd);
            if (ExecLinux.Cmd.ExitCode != 0)
            {
                var error = ExecLinux.Cmd.ErrorText.ToString();
                if (error.Contains("ORA-39002") && error.Contains("ORA-39168") && Excludes.Contains("USER"))
                {
                    ExecLinux.Cmd.OutputHandler?.Invoke("提示：遇到ORA-39002和ORA-39168错误，而且参数包含exclude=USER，" +
                        "怀疑是导出dmp的用户无dba权限，下面将去掉exclude=USER重试一次。");
                    cmd = $"source .bash_profile>/dev/null; impdp " + GetParam(true);
                    ExecLinux.ExecuteCommand(cmd);
                }
            }
        }

        public string GetParam(bool removeExcludeUserOption)
        {
            string remap_schema = "";
            string remap_tablespace = "";//去除了表空间配置，因此默认表空间和用户名相同
            if (DumpFileSchema != OracleUser)
            {
                remap_schema = $" remap_schema={DumpFileSchema}:{OracleUser}";

                remap_tablespace = $" remap_tablespace={DumpFileSchema}:{OracleUser}";
            }
            string exclude = GetExcludeParam(removeExcludeUserOption);
            string jobName = $" job_name=tooljob_imp{DateTime.Now.ToString("yyyyMMdd_HHmmss")}";
            var userAndPwd = (string.IsNullOrWhiteSpace(OracleService)) ? 
                $"\"{OracleUser}/{OraclePassword}\"" : $"\"{OracleUser}/{OraclePassword}@{OracleService}\"";
            Exec.MaskWords.Add(userAndPwd);
            var param = $"{userAndPwd} directory={DumpFileDirectory} " +
                $"dumpfile={DumpFileName} schemas={DumpFileSchema} table_exists_action=replace " +
                $"transform=oid:n {exclude} {remap_schema}{remap_tablespace}{jobName}";
            return param;
        }

        public string GetExcludeParam(bool removeExcludeUserOption)
        {
            if (Excludes.Count == 0) return "";

            var s = new StringBuilder(128);
            foreach (var item in this.Excludes)
            {
                if (removeExcludeUserOption && item == "USER") continue;
                if (s.Length == 0)
                {
                    s.Append("exclude=");
                }
                else
                {
                    s.Append(",");
                }
                s.Append(item);
            }

            return s.ToString();
        }

    }
}
